# 插件名：自动重组
# 版本：1.0.4
# 作者：詹广权
# 版权所有：http://www.cdsjcc.cn/xgj
# 说明：智能解组到合适层级再重新打组，保留内部无嵌套的组结构，一键自动化
#       对于只包含面的单层组，提供强制重新打组选项

require 'extensions.rb'

# 注册扩展
extension = SketchupExtension.new("自动重组", "zdcz.rb")
extension.description = "智能解组到合适层级再重新打组，保留内部无嵌套的组结构，一键自动化操作工具"
extension.version = "1.0.4"
extension.creator = "詹广权"
extension.copyright = "版权所有：http://www.cdsjcc.cn/xgj"
Sketchup.register_extension(extension, true)

module AutoReGroup
  module Plugin

    # 进度对话框实例
    @@progress_dialog = nil
    @@start_time = nil
    @@processed_count = 0
    @@total_count = 0
    @@operation_aborted = false
    @@processed_group = nil

    # 添加菜单项
    unless file_loaded?(__FILE__)
      UI.menu("Plugins").add_item("自动重组") {
        single_ungroup_regroup_with_progress
      }
      file_loaded(__FILE__)
    end

    # 创建进度对话框
    def self.create_progress_dialog
      return @@progress_dialog if @@progress_dialog && @@progress_dialog.visible?
      
      options = {
        :dialog_title => "自动重组进度",
        :style => UI::HtmlDialog::STYLE_DIALOG,
        :width => 460,
        :height => 350,
        :resizable => false,
        :min_width => 460,
        :min_height => 350,
        :max_width => 460,
        :max_height => 350
      }
      
      @@progress_dialog = UI::HtmlDialog.new(options)
      
      # 设置HTML文件路径
      html_file = File.join(__dir__, 'zdcz', 'progress.html')
      @@progress_dialog.set_file(html_file)
      
      # 添加回调函数
      @@progress_dialog.add_action_callback("dialogReady") { |action_context|
        puts "进度对话框已准备就绪"
      }
      
      @@progress_dialog.add_action_callback("closeDialog") { |action_context|
        @@progress_dialog.close if @@progress_dialog
      }
      
      @@progress_dialog.add_action_callback("abortOperation") { |action_context|
        puts "用户请求中止操作"
        @@operation_aborted = true
        show_aborted
      }
      
      @@progress_dialog.center
      @@progress_dialog
    end
    
    # 更新进度
    def self.update_progress(progress, status, current_item = "")
      return unless @@progress_dialog && @@progress_dialog.visible?
      
      # 使用字符串插值和转义确保兼容性
      escaped_status = status.gsub('"', '\\"').gsub("\n", "\\n")
      escaped_item = current_item.gsub('"', '\\"').gsub("\n", "\\n")
      
      # 手动构建JavaScript对象字符串，避免to_json兼容性问题
      script = "updateProgress({" +
               "progress: #{progress}, " +
               "status: \"#{escaped_status}\", " +
               "currentItem: \"#{escaped_item}\", " +
               "processedCount: #{@@processed_count}, " +
               "totalCount: #{@@total_count}, " +
               "isCompleted: #{progress >= 100 ? 'true' : 'false'}, " +
               "isError: false, " +
               "isAborted: false" +
               "});"
      @@progress_dialog.execute_script(script)
    end
    
    # 显示错误
    def self.show_error(error_message)
      return unless @@progress_dialog && @@progress_dialog.visible?
      
      # 使用字符串插值和转义确保兼容性
      escaped_message = error_message.gsub('"', '\\"').gsub("\n", "\\n")
      
      # 手动构建JavaScript对象字符串，避免to_json兼容性问题
      script = "updateProgress({" +
               "progress: 0, " +
               "status: \"#{escaped_message}\", " +
               "currentItem: \"\", " +
               "processedCount: #{@@processed_count}, " +
               "totalCount: #{@@total_count}, " +
               "isCompleted: false, " +
               "isError: true, " +
               "isAborted: false" +
               "});" 
      @@progress_dialog.execute_script(script)
    end
    
    # 显示中止状态
    def self.show_aborted
      return unless @@progress_dialog && @@progress_dialog.visible?
      
      # 手动构建JavaScript对象字符串
      script = "updateProgress({" +
               "progress: #{@@processed_count.to_f / @@total_count * 100}, " +
               "status: \"操作已被用户中止\", " +
               "currentItem: \"\", " +
               "processedCount: #{@@processed_count}, " +
               "totalCount: #{@@total_count}, " +
               "isCompleted: false, " +
               "isError: false, " +
               "isAborted: true" +
               "});" 
      @@progress_dialog.execute_script(script)
    end
    
    # 关闭进度对话框
    def self.close_progress_dialog
      if @@progress_dialog && @@progress_dialog.visible?
        @@progress_dialog.close
        @@progress_dialog = nil
      end
    end

    # 带进度显示的自动重组操作
    def self.single_ungroup_regroup_with_progress
      model = Sketchup.active_model
      selection = model.selection
      
      if selection.empty?
        UI.messagebox("请先选择要处理的组或组件")
        return
      end
      
      # 重置中止标志
      @@operation_aborted = false
      
      # 创建并显示进度对话框
      dialog = create_progress_dialog
      dialog.show
      
      # 初始化进度变量
      @@start_time = Time.now
      @@processed_count = 0
      @@total_count = 1  # 单个实体处理
      
      # 延迟执行主要操作，确保对话框完全加载
      UI.start_timer(0.5, false) {
        perform_ungroup_regroup_operation(model, selection)
      }
    end
    
    # 执行实际的自动重组操作（异步处理）
    def self.perform_ungroup_regroup_operation(model, selection)
      # 分析选中的实体
      entity_to_process = nil
      selection.each do |entity|
        if entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
          entity_to_process = entity
          break
        end
      end
      
      unless entity_to_process
        show_error("请选择一个组或组件进行处理")
        return
      end
      
      # 检查是否只包含面（一层外组）
      if contains_only_faces?(entity_to_process)
        # 关闭进度对话框
        close_progress_dialog
        
        # 提示用户是否强制重新打组
        result = UI.messagebox("检测到选中的组只包含面，无需重新打组。\n\n是否强制重新打组？", MB_YESNO)
        
        if result == IDNO
          puts "用户取消了强制重新打组操作"
          return
        else
          puts "用户选择强制重新打组"
          # 重新显示进度对话框
          dialog = create_progress_dialog
          dialog.show
          UI.start_timer(0.5, false) {
            process_entity_async(model, selection, entity_to_process, 0)
          }
          return
        end
      end
      
      # 开始异步处理
      process_entity_async(model, selection, entity_to_process, 0)
    end
    
    # 异步处理实体的各个阶段
    def self.process_entity_async(model, selection, entity_to_process, stage)
      # 检查是否已中止
      if @@operation_aborted
        puts "异步处理被中止，阶段: #{stage}"
        show_aborted
        model.abort_operation
        return
      end
      
      case stage
      when 0
        # 阶段0：开始操作
        model.start_operation("自动重组操作", true)
        update_progress(10, "正在分析选中的实体...")
        UI.start_timer(0.1, false) { 
          # 定时器回调中检查中止状态
          if @@operation_aborted
            puts "定时器回调检测到中止，阶段1"
            show_aborted
            model.abort_operation
            return
          end
          process_entity_async(model, selection, entity_to_process, 1) 
        }
        
      when 1
        # 阶段1：开始处理
        update_progress(20, "开始处理实体", "#{entity_to_process.class} (ID: #{entity_to_process.entityID})")
        puts "开始处理: #{entity_to_process.class} (ID: #{entity_to_process.entityID})"
        UI.start_timer(0.1, false) { 
          # 定时器回调中检查中止状态
          if @@operation_aborted
            puts "定时器回调检测到中止，阶段2"
            show_aborted
            model.abort_operation
            return
          end
          process_entity_async(model, selection, entity_to_process, 2) 
        }
        
      when 2
        # 阶段2：处理嵌套结构
        update_progress(40, "正在解组嵌套结构...")
        parent_entities = entity_to_process.parent.entities
        
        begin
          # 在开始处理前再次检查中止状态
          if @@operation_aborted
            puts "阶段2开始前检测到中止"
            show_aborted
            model.abort_operation
            return
          end
          
          new_group = process_entity_with_progress(entity_to_process, parent_entities)
          
          # 处理完成后检查是否被中止
          if @@operation_aborted
            puts "阶段2处理完成后检测到中止"
            show_aborted
            model.abort_operation
            return
          end
          
          @@processed_group = new_group
          UI.start_timer(0.1, false) { 
            # 定时器回调中检查中止状态
            if @@operation_aborted
              puts "定时器回调检测到中止，阶段3"
              show_aborted
              model.abort_operation
              return
            end
            process_entity_async(model, selection, entity_to_process, 3) 
          }
        rescue => e
          puts "处理过程中出错: #{e.message}"
          show_error("处理过程中出错: #{e.message}")
          model.abort_operation
        end
        
      when 3
        # 阶段3：完成处理
        if @@operation_aborted
          model.abort_operation
          return
        end
        
        update_progress(80, "正在整理结果...")
         
         # 选中新创建的组
         if @@processed_group && @@processed_group.valid?
           selection.clear
           selection.add(@@processed_group)
           puts "已选中新创建的组"
           @@processed_count = 1
           update_progress(100, "操作完成")
           puts "自动重组操作完成"
           model.commit_operation
         else
           show_error("处理失败，未能创建有效的组")
           model.abort_operation
         end
         
       end # case结束
     end # process_entity_async方法结束
    
    # 单个自动重组操作（保留原有功能，无进度显示）
    def self.single_ungroup_regroup
      model = Sketchup.active_model
      selection = model.selection
      
      if selection.empty?
        UI.messagebox("请先选择要处理的组或组件")
        return
      end
      
      model.start_operation("单个自动重组", true)
      
      begin
        # 只处理第一个有效的组或组件
        entity_to_process = nil
        selection.each do |entity|
          if entity.is_a?(Sketchup::Group) || entity.is_a?(Sketchup::ComponentInstance)
            entity_to_process = entity
            break
          end
        end
        
        if entity_to_process
          puts "开始处理: #{entity_to_process.class} (ID: #{entity_to_process.entityID})"
          parent_entities = entity_to_process.parent.entities
          new_group = process_entity(entity_to_process, parent_entities)
          
          # 选中新创建的组
          selection.clear
          if new_group && new_group.valid?
            selection.add(new_group)
            puts "已选中新创建的组"
          end
          
          puts "单个自动重组操作完成"
        else
          puts "未找到有效的组或组件"
        end
        
      rescue => e
        puts "错误: #{e.message}"
        puts e.backtrace
        model.abort_operation
        return
      end
      
      model.commit_operation
    end
    
    # 带进度更新的实体处理
    def self.process_entity_with_progress(entity, parent_entities)
      return nil unless entity.valid?
      
      puts "  -> 开始处理实体: #{entity.class}"
      update_progress(50, "正在处理嵌套结构...", entity.class.to_s)
      
      # 直接处理嵌套结构（变换已在process_nested_structure中处理）
      new_group = process_nested_structure_with_progress(entity, parent_entities)
      
      if new_group && new_group.valid?
        puts "  -> 处理成功，创建了新组"
        update_progress(70, "处理成功，已创建新组")
        return new_group
      else
        puts "  -> 处理失败"
        return nil
      end
    end
    
    # 处理单个实体（组或组件）- 原有方法保留
    def self.process_entity(entity, parent_entities)
      return nil unless entity.valid?
      
      puts "  -> 开始处理实体: #{entity.class}"
      
      # 直接处理嵌套结构（变换已在process_nested_structure中处理）
      new_group = process_nested_structure(entity, parent_entities)
      
      if new_group && new_group.valid?
        puts "  -> 处理成功，创建了新组"
        return new_group
      else
        puts "  -> 处理失败"
        return nil
      end
    end
    
    # 带进度更新的嵌套结构处理
    def self.process_nested_structure_with_progress(entity, target_entities)
      return nil unless entity.valid?
      
      # 检查是否已中止
      if @@operation_aborted
        puts "    -> 嵌套结构处理开始前检测到中止"
        show_aborted
        return nil
      end
      
      # 保存原始变换
      original_transform = entity.transformation
      
      # 检查是否应该停止解组（新逻辑）
      if should_stop_ungrouping?(entity)
        puts "    -> 检测到应该停止解组的层级，直接炸开并重新打组"
        update_progress(55, "到达目标层级，正在重新打组...")
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 炸开前检测到中止"
          show_aborted
          return nil
        end
        
        # 炸开当前实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 过滤有效实体（包括组、组件和基础几何体）
        valid_entities = exploded_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face) || e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 创建新组前检测到中止"
          show_aborted
          return nil
        end
        
        update_progress(65, "正在创建新组...")
        
        # 创建新组（保留内部的组和组件结构）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体（保留内部组结构）"
        return new_group
      end
      
      # 检查实体内部是否还有组或组件（继续原有逻辑）
      if has_nested_groups_or_components?(entity)
        puts "    -> 发现嵌套结构，开始递归处理"
        update_progress(55, "发现嵌套结构，正在递归处理...")
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 炸开前检测到中止"
          show_aborted
          return nil
        end
        
        # 炸开当前实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 注意：explode后的实体已经在正确的世界坐标位置
        # 不需要再次应用变换，因为explode会自动处理变换
        
        update_progress(60, "正在处理炸开后的实体...")
        
        # 炸开后再次检查中止状态
         if @@operation_aborted
           puts "    -> 炸开后检测到中止"
           show_aborted
           return nil
         end
        
        # 遍历炸开后的实体
        new_entities = []
        exploded_entities.each_with_index do |exploded_entity, index|
          next unless exploded_entity.valid?
          
          # 检查是否已中止 - 在每个实体处理前检查
          if @@operation_aborted
            puts "    -> 用户中止操作，停止处理"
            show_aborted
            return nil
          end
          
          # 更新进度（在处理多个实体时）
          if exploded_entities.length > 1
            progress = 60 + (index.to_f / exploded_entities.length * 15)
            update_progress(progress, "正在处理第#{index + 1}/#{exploded_entities.length}个实体...")
          end
          
          if exploded_entity.is_a?(Sketchup::Group) || exploded_entity.is_a?(Sketchup::ComponentInstance)
            # 递归处理嵌套的组或组件
            processed = process_nested_structure_with_progress(exploded_entity, target_entities)
            # 如果递归处理返回nil（可能是因为中止），立即返回
            if processed.nil? && @@operation_aborted
              puts "    -> 递归处理被中止"
              return nil
            end
            new_entities << processed if processed
          else
            # 添加基础几何体
            new_entities << exploded_entity
          end
        end
        
        # 过滤有效实体
        valid_entities = new_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face) || e.is_a?(Sketchup::Group)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 创建新组前检测到中止"
          show_aborted
          return nil
        end
        
        update_progress(65, "正在创建新组...")
        
        # 创建新组（几何体已经在正确位置）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体"
        return new_group
      else
        puts "    -> 没有嵌套结构，直接处理"
        update_progress(58, "没有嵌套结构，直接处理...")
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 直接处理前检测到中止"
          show_aborted
          return nil
        end
        
        # 炸开实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 注意：explode后的实体已经在正确的世界坐标位置
        # explode方法会自动将变换应用到几何体上
        
        # 过滤有效实体
        valid_entities = exploded_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 检查是否已中止
        if @@operation_aborted
          puts "    -> 直接处理创建新组前检测到中止"
          show_aborted
          return nil
        end
        
        update_progress(65, "正在创建新组...")
        
        # 创建新组（几何体已经在正确位置）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体"
        return new_group
      end
    end
    
    # 递归处理嵌套结构，保持层级关系 - 原有方法保留
    def self.process_nested_structure(entity, target_entities)
      return nil unless entity.valid?
      
      # 保存原始变换
      original_transform = entity.transformation
      
      # 检查是否应该停止解组（新逻辑）
      if should_stop_ungrouping?(entity)
        puts "    -> 检测到应该停止解组的层级，直接炸开并重新打组"
        
        # 炸开当前实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 过滤有效实体（包括组、组件和基础几何体）
        valid_entities = exploded_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face) || e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 创建新组（保留内部的组和组件结构）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体（保留内部组结构）"
        return new_group
      end
      
      # 检查实体内部是否还有组或组件（继续原有逻辑）
      if has_nested_groups_or_components?(entity)
        puts "    -> 发现嵌套结构，开始递归处理"
        
        # 炸开当前实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 注意：explode后的实体已经在正确的世界坐标位置
        # 不需要再次应用变换，因为explode会自动处理变换
        
        # 遍历炸开后的实体
        new_entities = []
        exploded_entities.each do |exploded_entity|
          next unless exploded_entity.valid?
          
          if exploded_entity.is_a?(Sketchup::Group) || exploded_entity.is_a?(Sketchup::ComponentInstance)
            # 递归处理嵌套的组或组件
            processed = process_nested_structure(exploded_entity, target_entities)
            new_entities << processed if processed
          else
            # 添加基础几何体
            new_entities << exploded_entity
          end
        end
        
        # 过滤有效实体
        valid_entities = new_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face) || e.is_a?(Sketchup::Group)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 创建新组（几何体已经在正确位置）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体"
        return new_group
      else
        puts "    -> 没有嵌套结构，直接处理"
        
        # 炸开实体
        exploded_entities = entity.explode
        return nil unless exploded_entities
        
        # 注意：explode后的实体已经在正确的世界坐标位置
        # explode方法会自动将变换应用到几何体上
        
        # 过滤有效实体
        valid_entities = exploded_entities.select { |e| e && e.valid? && (e.is_a?(Sketchup::Edge) || e.is_a?(Sketchup::Face)) }
        
        if valid_entities.empty?
          puts "    -> 没有有效实体可以重新组合"
          return nil
        end
        
        # 创建新组（几何体已经在正确位置）
        new_group = target_entities.add_group(valid_entities)
        puts "    -> 创建新组，包含 #{valid_entities.length} 个实体"
        return new_group
      end
    end
    
    # 检查实体内部是否有嵌套的组或组件
    def self.has_nested_groups_or_components?(entity)
      return false unless entity.valid?
      
      begin
        if entity.is_a?(Sketchup::Group)
          entities = entity.entities
        elsif entity.is_a?(Sketchup::ComponentInstance)
          entities = entity.definition.entities
        else
          return false
        end
        
        # 检查是否包含组或组件
        entities.each do |ent|
          if ent.is_a?(Sketchup::Group) || ent.is_a?(Sketchup::ComponentInstance)
            return true
          end
        end
        
        return false
      rescue => e
        puts "    -> 检查嵌套时出错: #{e.message}"
        return false
      end
    end
    
    # 检查实体是否应该停止解组（新增方法）
    # 如果实体内部的组或组件都不再包含嵌套的组或组件，则应该停止解组
    def self.should_stop_ungrouping?(entity)
      return true unless entity.valid?
      
      begin
        if entity.is_a?(Sketchup::Group)
          entities = entity.entities
        elsif entity.is_a?(Sketchup::ComponentInstance)
          entities = entity.definition.entities
        else
          return true
        end
        
        # 检查内部的每个组或组件是否还有嵌套
        entities.each do |ent|
          if ent.is_a?(Sketchup::Group) || ent.is_a?(Sketchup::ComponentInstance)
            # 如果发现内部的组或组件还有嵌套，则不应该停止解组
            if has_nested_groups_or_components?(ent)
              return false
            end
          end
        end
        
        # 如果所有内部的组或组件都没有嵌套，则应该停止解组
        return true
      rescue => e
        puts "    -> 检查是否应该停止解组时出错: #{e.message}"
        return true
      end
    end
    
    # 检查实体是否只包含面（没有嵌套组或组件）
    def self.contains_only_faces?(entity)
      return false unless entity.valid?
      
      begin
        if entity.is_a?(Sketchup::Group)
          entities = entity.entities
        elsif entity.is_a?(Sketchup::ComponentInstance)
          entities = entity.definition.entities
        else
          return false
        end
        
        # 检查是否包含任何组或组件
        entities.each do |ent|
          if ent.is_a?(Sketchup::Group) || ent.is_a?(Sketchup::ComponentInstance)
            return false
          end
        end
        
        # 检查是否至少包含一个面
        has_faces = false
        entities.each do |ent|
          if ent.is_a?(Sketchup::Face)
            has_faces = true
            break
          end
        end
        
        return has_faces
      rescue => e
        puts "    -> 检查是否只包含面时出错: #{e.message}"
        return false
      end
    end
    
    # 安全的解组方法（避免崩溃）
    def self.safe_explode(entity)
      return nil unless entity.valid?
      
      begin
        # 对于组，先转换为组件以避免某些版本的崩溃问题
        if entity.is_a?(Sketchup::Group)
          # 根据论坛讨论，某些情况下组的解组可能导致崩溃
          # 转换为组件可以避免这个问题
          component = entity.to_component
          return component.explode if component && component.valid?
        else
          return entity.explode
        end
      rescue => e
        puts "    -> 安全解组失败: #{e.message}"
        return nil
      end
    end

  end # module Plugin
end # module AutoReGroup

# 加载工具栏模块
begin
  require_relative 'zdcz/gjl.rb'
rescue => e
  puts "加载工具栏模块失败: #{e.message}"
end

puts "自动重组插件已加载"